package com.minisoft.utils;

import com.google.common.base.CaseFormat;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.cache.*;
import com.google.common.collect.*;
import com.google.common.util.concurrent.RateLimiter;
import org.junit.Test;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author JueSu
 * @description: TODO
 * @date 2022/7/8 16:49
 */
public class GuavaTest {

    @Test
    public void testImmutable() {
        //基于已有的集合创建不可变集合
        List<String> list  = new ArrayList<String>();
        list.add("a");
        list.add("b");
        list.add("c");
        ImmutableList<String> immutList = ImmutableList.copyOf(list);
        list.add("d");
        System.out.println(immutList); //[a,b,c]

        ImmutableList<String> immutableList = ImmutableList.of("a","b","c");

        //对于有序的不可变集合来说，是在集合构造完成时就已经排序完成
        ImmutableSortedSet<String> of = ImmutableSortedSet.of("a", "b", "c", "A", "d", "B");
        System.out.println(of);

    }

    @Test
    public void testCollection() {
        // 创建一个 ArrayList 集合
        List<String> list1 = Lists.newArrayList();
        // 创建一个 ArrayList 集合，同时塞入3个数据
        List<String> list2 = Lists.newArrayList("a", "b", "c");
        //反转list
        List<String> reverseList = Lists.reverse(list2);
        //把list中的每个元素拼接一个1
        List<String> listtr = Lists.transform(list2,str -> str + "1");
        // 创建一个 ArrayList 集合，容量初始化为10
        List<String> list3 = Lists.newArrayListWithCapacity(10);

        LinkedList<String> linkedList1 = Lists.newLinkedList();
        CopyOnWriteArrayList<String> cowArrayList = Lists.newCopyOnWriteArrayList();

        HashMap<Object, Object> hashMap = Maps.newHashMap();
        ConcurrentMap<Object, Object> concurrentMap = Maps.newConcurrentMap();
        TreeMap<Comparable, Object> treeMap = Maps.newTreeMap();

        HashSet<Object> hashSet = Sets.newHashSet();
        HashSet<String> newHashSet = Sets.newHashSet("a", "a", "b", "c");

        Set<String> newHashSet1 = Sets.newHashSet("a", "a", "b", "c");
        Set<String> newHashSet2 = Sets.newHashSet("b", "b", "c", "d");

        // 交集
        Sets.SetView<String> intersectionSet = Sets.intersection(newHashSet1, newHashSet2);
        System.out.println(intersectionSet); // [b, c]

        // 并集
        Sets.SetView<String> unionSet = Sets.union(newHashSet1, newHashSet2);
        System.out.println(unionSet); // [a, b, c, d]

        // newHashSet1 中存在，newHashSet2 中不存在
        Sets.SetView<String> setView = Sets.difference(newHashSet1, newHashSet2);
        System.out.println(setView); // [a]

        // Java 统计相同元素出现的次数。
        ArrayList<String> arrayList = Lists.newArrayList("a", "b", "c", "d", "a", "c");
        HashMultiset<String> multiset1 = HashMultiset.create(arrayList);
        multiset1.elementSet().forEach(s -> System.out.println(s + ":" + multiset1.count(s)));
        //一对多，value 是 List 的 Map 集合。
        ArrayListMultimap<String, String> multimap = ArrayListMultimap.create();
        multimap.put("狗", "大黄");
        multimap.put("狗", "旺财");
        multimap.put("猫", "加菲");
        multimap.put("猫", "汤姆");
        multimap.put("猫", "汤姆");
        System.out.println(multimap.get("猫")); // [加菲, 汤姆]

        //value是Set 的 Map 集合
        // HashMultimap<String, String> multimap = HashMultimap.create();
//通过create()方法创建
        Multiset<String> multiset = HashMultiset.create();
        //可直接添加元素
        multiset.add("a");
        multiset.add("b");
        multiset.add("c");
        multiset.add("c");
        multiset.add("c");
        List<String> list = new ArrayList<String>();
        list.add("xx");
        list.add("yy");
        list.add("zz");
        //也可用addAll方法添加集合进来
        multiset.addAll(list);

        //获取元素"c"的计数
        System.out.println(multiset.count("c"));

        //返回去重后的元素set集合
        Set<String> set = multiset.elementSet();

        //multiset所有元素的个数
        System.out.println("multiset.size():" + multiset.size());
        //multiset去重后的元素个数
        System.out.println("elementSet().size():" + multiset.elementSet().size());

        //元素迭代
        Iterator<String> it = multiset.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        //可以通过设置元素的计数，来批量的添加元素，当然能加也能减
        multiset.setCount("c",5);

        //将元素的计数设为0，就相当于移除所有的"c"元素
        multiset.setCount("c",0);

        //移除一个元素
        multiset.remove("c");

        //移除两个"c"元素
        multiset.remove("c",2);


        SortedMultiset<String> sortedMultiset = TreeMultiset.create();
        sortedMultiset.setCount("c",5);
        sortedMultiset.setCount("a",3);
        sortedMultiset.setCount("b",2);
        //获取第一个元素
        sortedMultiset.firstEntry().getElement();
        //获了最后一个元素
        sortedMultiset.lastEntry().getElement();
        //获取子集
        SortedMultiset<String> subMultiset = sortedMultiset.subMultiset("a", BoundType.OPEN,"b",BoundType.CLOSED);
        System.out.println(subMultiset);
    }

    @Test
    public void testMultiMap() {
        Multimap multimap = ArrayListMultimap.create();
//新增元素,直接put
        multimap.put("a","123");
        multimap.put("a","111");
        multimap.put("b","456");
        multimap.put("d","789");

        Multimap multimap1 = LinkedListMultimap.create();
        multimap1.put("a","a1_value");
        multimap1.put("k2","k2_value");
//使用putAll方法可以添加一个multimap，这个跟JDK中的putAll一样，而且key相同时会进行合并
        multimap.putAll(multimap1);

        List<String> list = Lists.newArrayList("a_value1","a_value2","a_value3");
//还可以指定key进行批量添加元素，注意此处是追加到key中，不是替换
        multimap.putAll("a",list);

//multimap中的所有键值对，重复的算多个
        System.out.println(multimap.size());
//key的个数
        System.out.println(multimap.keySet().size());

//移除指定key的指定value
        multimap.remove("a","111");
        System.out.println(multimap);
//移除整个key的所有value
        multimap.removeAll("a");
        System.out.println(multimap);


//替换指定key的value
        multimap.replaceValues("b",Lists.newArrayList("b1_value","b2_value"));

//是否包含指定的key
        System.out.println(multimap.containsKey("d"));
//是否包含指定的键值对
        System.out.println(multimap.containsEntry("d","789"));
//获取multimap中所有的value
        System.out.println(multimap.values());
//返回Multiset
        System.out.println(multimap.keys());
//返回Map类型
        Map<String,List<String>> map = multimap.asMap();

//清空整个集合
        multimap.clear();

        System.out.println(multimap);
    }

    @Test
    public void testBiMap() {
        //BiMap，它是一种特殊的Map，可以实现键值的反转
        //因为BiMap要支持反转，所以它的key和value都必须是唯一的，要不然反转过来就存在一对多的情况
        BiMap biMap = HashBiMap.create();
        biMap.put("a","123");
        System.out.println(biMap);
        //对键值对进行反转
        System.out.println(biMap.inverse());

        //试图将一个key映射到已经存在的值上，会抛异常
        biMap.put("b","123");

        //强值将一个key映射到已经存在的值上，会将原来的key覆盖掉
        biMap.forcePut("b","123");
        System.out.println(biMap);

    }

    @Test
    public void testTable() {
//创建row,column,value结构的table
        Table<String,String,Integer> table = HashBasedTable.create();
        table.put("a1","c1",23);
        table.put("a1","c2",77);
        table.put("a2","c2",44);
//通过rowKey获取columnKey->value的映射关系
        System.out.println(table.row("a1"));//{c1=23, c2=77}
//通过columnKey获取rowKey ->value的映射关系
        System.out.println(table.column("c2"));//{a1=77, a2=44}
    }

    @Test
    public void testStr() {
        //字符拼接
        ArrayList<String> list = Lists.newArrayList("a", "b", "c", null);
        String join = Joiner.on(",").skipNulls().join(list);
        System.out.println(join); // a,b,c

        String join1 = Joiner.on(",").useForNull("空值").join("旺财", "汤姆", "杰瑞", null);
        System.out.println(join1); // 旺财,汤姆,杰瑞,空值

        //字符串分割
        String str5 = ",a ,,b ,";
        Iterable<String> split = Splitter.on(",")
                .omitEmptyStrings() // 忽略空值
                .trimResults() // 过滤结果中的空白
                .split(str5);
        split.forEach(System.out::println);

        //提取字符串中的小写字母
        String s = "er 3j6o  3k  ,)$ wt@ wr4576je  ow3453535345irjew jwfel ";
        //返回只保留a-z的字母的字符中
        String str1 = CharMatcher.inRange('a','z').retainFrom(s);
        System.out.println(str1);

        String s2 = "er 3j6o  3k  ,)$ wt@ wr4576je  ow3453535345irjew jwfel ";
        //去掉字符串中数字
        String str2 = CharMatcher.digit().removeFrom(s);
        CharMatcher.inRange('0','9');
        System.out.println(str5);
        String str = "aj\tld1\b23aAbCs  kF45JAb  c56sl";
        //移除str中的a
        CharMatcher.is('a').removeFrom(str);
        //移除str中的a
        CharMatcher.isNot('a').retainFrom(str);
        //保留str中的a,b,c字符
        CharMatcher.anyOf("abc").retainFrom(str);


        //保留str中的a,b,c字符
        CharMatcher.noneOf("abc").removeFrom(str);
        //匹配str中的a-j的字母，全部替换成数字6
        CharMatcher.inRange('a','j').replaceFrom(str,"6");
        //去str中的空格
        CharMatcher.breakingWhitespace().removeFrom(str);
        //去掉str中的数字
        CharMatcher.digit().removeFrom(str);
        //去掉控制字符(\t,\n,\b...)
        CharMatcher.javaIsoControl().removeFrom(str);
        //获取str中的小写字母
        CharMatcher.javaLowerCase().retainFrom(str);
        CharMatcher.inRange('a','z').retainFrom(str);
        //获取str中的大写字母
        CharMatcher.javaUpperCase().retainFrom(str);

        //组合条件：获取str中的大写字母和数字
        System.out.println(CharMatcher.javaUpperCase().or(CharMatcher.digit()).retainFrom(str));


        String str3 = "hello_world";
        CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,str3);//转换成helloWorld
        CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL,str3);//转换成HelloWorld
    }

    @Test
    public void testRateLimiter() {
//每秒5个令牌
        RateLimiter rateLimiter = RateLimiter.create(5);
        while(true){
            //平均每个0.2秒左右，很均匀
            //当产生令牌的速率大于取令牌的速率时，是不需要等待令牌时间的
            System.out.println("time:" + rateLimiter.acquire() + "s");

            //一次取出5个令牌也可以快速响应
            System.out.println("time:" + rateLimiter.acquire(5) + "s");

        }

        //平滑预热限流
        //这种方式适用于系统启动后需要一段时间来进行预热的场景
        //比如，我设置的是每秒5个令牌，预热期为5秒，那么它就不会是0.2左右产生一个令牌。在前5秒钟它不是一个均匀的速率，5秒后恢复均匀的速率
        //每秒5个令牌，预热期为5秒
//        RateLimiter rateLimiter2 = RateLimiter.create(5,5, TimeUnit.SECONDS);
//        while(true){
//            //一次取出5个令牌也可以快速响应
//            System.out.println("time:" + rateLimiter2.acquire(1) + "s");
//            System.out.println("time:" + rateLimiter2.acquire(1) + "s");
//            System.out.println("time:" + rateLimiter2.acquire(1) + "s");
//            System.out.println("time:" + rateLimiter2.acquire(1) + "s");
//            System.out.println("time:" + rateLimiter2.acquire(1) + "s");
//            System.out.println("-----------");
//        }
    }

    @Test
    public void testCache() throws InterruptedException, ExecutionException {
        CacheLoader cacheLoader = new CacheLoader<String, Animal>() {
            // 如果找不到元素，会调用这里
            @Override
            public Animal load(String s) {
                return null;
            }
        };
        LoadingCache<String, Animal> loadingCache = CacheBuilder.newBuilder()
                .maximumSize(1000) // 容量
                .expireAfterWrite(3, TimeUnit.SECONDS) // 过期时间
                .removalListener(new MyRemovalListener()) // 失效监听器
                .build(cacheLoader); //
        loadingCache.put("狗", new Animal("旺财", 1));
        loadingCache.put("猫", new Animal("汤姆", 3));
        loadingCache.put("狼", new Animal("灰太狼", 4));

        loadingCache.invalidate("猫"); // 手动失效

        Animal animal = loadingCache.get("狼");
        System.out.println(animal);
        Thread.sleep(4 * 1000);
        // 狼已经自动过去，获取为 null 值报错
        System.out.println(loadingCache.get("狼"));
        /**
         * key=猫,value=Animal{name='汤姆', age=3},reason=EXPLICIT
         * Animal{name='灰太狼', age=4}
         * key=狗,value=Animal{name='旺财', age=1},reason=EXPIRED
         * key=狼,value=Animal{name='灰太狼', age=4},reason=EXPIRED
         *
         * com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key 狼.
         */
    }

    /**
     * 缓存移除监听器
     */
    class MyRemovalListener implements RemovalListener<String, Animal> {

        @Override
        public void onRemoval(RemovalNotification<String, Animal> notification) {
            String reason = String.format("key=%s,value=%s,reason=%s", notification.getKey(), notification.getValue(), notification.getCause());
            System.out.println(reason);
        }
    }

    class Animal {
        private String name;
        private Integer age;

        @Override
        public String toString() {
            return "Animal{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }

        public Animal(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    }

    @Test
    public void cacheCreateTest(){
        Cache<String,String> cache = CacheBuilder.newBuilder()
                .maximumSize(100) //设置缓存最大容量
                .expireAfterWrite(1,TimeUnit.MINUTES) //过期策略，写入一分钟后过期
                .build();
        cache.put("a","a1");
        String value = cache.getIfPresent("a");
    }

    @Test
    public void testEvent() {
        //https://zhuanlan.zhihu.com/p/164895790
    }
}
